package com.asyf.demo.controller;

import com.asyf.demo.config.mydefined.ConnectionLeakInfo;
import com.asyf.demo.config.mydefined.MyConnectionLeakProxy;
import com.asyf.demo.entity.DataSourceDto;
import com.asyf.demo.mapper.UserMapper;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.ds.ItemDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.BeanUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.sql.Connection;
import java.util.*;

/**
 * @Author yjz
 * @Date 2021/8/18 10:17
 */
@RestController
@RequestMapping(value = "/config")
@Api(tags = "数据库配置")
public class ConfigController {

    @Resource
    private UserMapper userMapper;

    @Resource
    private DataSource dataSource;

    @Resource
    private DefaultDataSourceCreator dataSourceCreator;

    @GetMapping("list")
    public Set<String> list() {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        Map<String, DataSource> dataSources = ds.getDataSources();
        return ds.getDataSources().keySet();
    }

    @PostMapping("add")
    public Set<String> add(@Validated @RequestBody DataSourceDto dto) {
        DataSourceProperty dataSourceProperty = new DataSourceProperty();
        BeanUtils.copyProperties(dto, dataSourceProperty);
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
        ds.addDataSource(dto.getPollName(), dataSource);
        return ds.getDataSources().keySet();
    }

    @DeleteMapping("remove")
    public void remove(String name) {
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        ds.removeDataSource(name);
    }

    @GetMapping("/view")
    public Object view() throws Exception {
        Map<String, Object> result = new TreeMap<>();
        DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
        Map<String, DataSource> dataSources = ds.getDataSources();

        for (Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
            Map<String, Object> obj = getPoolInfo(entry.getValue());

            result.put(entry.getKey(), obj);
        }
        return result;
    }

    @ApiOperation(value = "获取泄露的连接", tags = "获取泄露的连接", notes = "即获取正在被使用的连接，可以记录一下开始时间")
    @RequestMapping(value = "/getLeakConnections", method = RequestMethod.GET)
    public Object getLeakConnections() throws Exception {
        Map<Connection, Throwable> map = MyConnectionLeakProxy.getLeakConnections();
        String newLine = "\r\n";
        StringBuilder buf = new StringBuilder();
        buf.append("开始检测数据库连接: 当前服务器 " + InetAddress.getLocalHost().getHostName()).append(newLine);
        if (map.isEmpty()) {
            buf.append("恭喜你, 无泄漏的数据库连接").append(newLine);
            return buf.toString();
        } else {
            buf.append("共发现 " + map.size() + " 个泄漏的数据库连接").append(newLine);
            buf.append("\r\n");
            Iterator var3 = map.entrySet().iterator();

            while (var3.hasNext()) {
                Map.Entry<Connection, Throwable> entry = (Map.Entry) var3.next();
                buf.append("=====  " + entry.getKey() + "  =====").append(newLine);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                ((Throwable) entry.getValue()).printStackTrace(pw);
                pw.close();
                buf.append(sw.toString());
            }

            return buf.toString();
        }
    }

    @ApiOperation(value = "获取泄露的连接2", tags = "获取泄露的连接", notes = "即获取正在被使用的连接，记录了连接耗时，以便筛选真正的泄露")
    @RequestMapping(value = "/getLeakConnections2", method = RequestMethod.GET)
    public Object getLeakConnections2() throws Exception {
        Map<Connection, ConnectionLeakInfo> map = MyConnectionLeakProxy.getLeakConnections2();
        String newLine = "\r\n";
        StringBuilder buf = new StringBuilder();
        buf.append("开始检测数据库连接: 当前服务器 " + InetAddress.getLocalHost().getHostName()).append(newLine);
        if (map.isEmpty()) {
            buf.append("恭喜你, 无泄漏的数据库连接").append(newLine);
            return buf.toString();
        } else {
            buf.append("共发现 " + map.size() + " 个泄漏的数据库连接").append(newLine);
            buf.append("\r\n");
            Iterator var3 = map.entrySet().iterator();

            while (var3.hasNext()) {
                Map.Entry<Connection, ConnectionLeakInfo> entry = (Map.Entry) var3.next();
                ConnectionLeakInfo connectionLeakInfo = entry.getValue();
                Throwable t = connectionLeakInfo.getT();

                buf.append("=====  " + entry.getKey() + "  =====").append(newLine);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                t.printStackTrace(pw);
                pw.close();
                buf.append("连接已使用时长（毫秒）:").append(connectionLeakInfo.getDiff()).append("\r\n");
                buf.append(sw.toString());
            }


            return buf.toString();
        }
    }

    private Map<String, Object> getPoolInfo(DataSource dataSource) throws Exception {
        Map<String, Object> result = new LinkedHashMap<>();

        result.put("dataSourceClass", dataSource.getClass());
        Field realDataSource = ItemDataSource.class.getDeclaredField("realDataSource");
        realDataSource.setAccessible(true);
        HikariDataSource hikariDataSource = (HikariDataSource) realDataSource.get(dataSource);

        Field poolField = HikariDataSource.class.getDeclaredField("pool");
        poolField.setAccessible(true);
        HikariPool pool = (HikariPool) poolField.get(hikariDataSource);

        Map<String, Object> poolMap = new TreeMap<>();
        if (pool != null) {
            if (hikariDataSource instanceof HikariDataSource) {
                poolMap.put("poolName", pool.toString());
                poolMap.put("activeConnections", pool.getActiveConnections());
                poolMap.put("idleConnections", pool.getIdleConnections());
                poolMap.put("maximumPoolSize", pool.config.getMaximumPoolSize());
                poolMap.put("minimumIdle", pool.config.getMinimumIdle());
                poolMap.put("idleTimeout", hikariDataSource.getIdleTimeout());
                result.put("pool", poolMap);
            } else {
                result.put("pool", "未知的连接池类型");
            }
        }

        return result;
    }

}
